package com.tools.common.thread;

import com.tools.common.exception.ThreadOperationException;
import com.tools.common.io.ProfileKit;
import com.tools.common.object.Note;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.*;

/**
 * 线程操作的工具类
 * */
@Note("线程操作的工具类")
public final class ThreadKit {

    private ThreadKit() {}

    @Note("获取 CPU 密集型的最佳核心线程数")
    public static int getCPUIntensiveType() {
        return Runtime.getRuntime().availableProcessors() + 1;
    }

    @Note("获取 IO 密集型的最佳核心线程数")
    public static int getIOIntensiveType() {
        return (Runtime.getRuntime().availableProcessors() * 2) + 1;
    }


    @Note("让当前线程睡眠 mill 毫秒")
    public static void sleep(long mill) {
        try {
            Thread.sleep(mill);
        } catch (InterruptedException e) {
            throw new ThreadOperationException(e);
        }
    }

    @Note("让当前线程睡眠 second 秒")
    public static void sleepSecond(long second) {
        try {
            long mill = Timeunit.toMill(second, Timeunit.SECOND);
            Thread.sleep(mill);
        } catch (InterruptedException e) {
            throw new ThreadOperationException(e);
        }
    }


    @Note("让当前线程睡眠 time + timeunit（单位） 的时间")
    public static void sleep(long time, Timeunit timeunit) {
        try {
            time = Timeunit.toMill(time, timeunit);
            Thread.sleep(time);
        } catch (InterruptedException e) {
            throw new ThreadOperationException(e);
        }
    }

    @Note("根据项目配置文件配置的参数，构建一个线程池")
    public static ThreadPoolExecutor newThreadPool() {
        ThreadPoolSource poolSource = ProfileKit.getThreadPoolSource();
        return newThreadPool(poolSource);
    }

    @Note("根据入参对象配置的参数，构建一个线程池")
    public static ThreadPoolExecutor newThreadPool(ThreadPoolSource poolSource) {
        if(poolSource == null) throw new IllegalArgumentException("构造线程池时，配置项包装对象 ThreadPoolSource 不能为 null");
        return new ThreadPoolExecutor(
                poolSource.getCoreSize(),
                poolSource.getMaxSize(),
                poolSource.getKeepAliveTime(),
                poolSource.getKeepAliveTimeunit(),
                poolSource.getWorkQueue(),
                poolSource.getThreadFactory(),
                poolSource.getHandler()
        );
    }

    @Note("根据项目配置文件配置的参数，构建一个 Spring 官方的 jar 包的线程池")
    public static ThreadPoolTaskExecutor newSpringThreadPool() {
        ThreadPoolSource poolSource = ProfileKit.getThreadPoolSource();
        return newSpringThreadPool(poolSource);
    }

    @Note("根据入参对象配置的参数，构建一个 Spring 官方的 jar 包的线程池")
    public static ThreadPoolTaskExecutor newSpringThreadPool(ThreadPoolSource poolSource) {
        if(poolSource == null) throw new IllegalArgumentException("构造 Spring 的线程池时，配置项包装对象 ThreadPoolSource 不能为 null");
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        taskExecutor.setCorePoolSize(poolSource.getCoreSize());
        taskExecutor.setMaxPoolSize(poolSource.getMaxSize());
        taskExecutor.setQueueCapacity(poolSource.getQueueCapacity());
        taskExecutor.setThreadNamePrefix(poolSource.getThreadPrefix());
        taskExecutor.setRejectedExecutionHandler(poolSource.getHandler());
        taskExecutor.initialize();
        return taskExecutor;
    }


    @Note("等待获取线程执行的结果。期间会一直阻塞")
    public static <T>T completableFutureGetValue(CompletableFuture<? extends T> future) {
        try {
            return future.get();
        } catch (InterruptedException | ExecutionException e) {
            throw new ThreadOperationException(e);
        }
    }

    @Note("等待获取线程执行的结果。期间会一直阻塞，直到时间到期还没获取到则抛异常")
    public static <T>T completableFutureGetValue(CompletableFuture<? extends T> future, long timeout, TimeUnit timeUnit) {
        try {
            return future.get(timeout, timeUnit);
        } catch (InterruptedException | ExecutionException | TimeoutException e) {
            throw new ThreadOperationException(e);
        }
    }

    @Note("等待获取线程执行的结果。期间会一直阻塞，直到时间到期还没获取到则抛异常")
    public static <T>T completableFutureGetValue(CompletableFuture<? extends T> future, long timeout, Timeunit timeunit) {
        return completableFutureGetValue(future, timeout, Timeunit.asTimeUnit(timeunit));
    }
}
